Skip to content

Add in-context media asset editing - #2905

Merged
khoinguyenpham04 merged 20 commits into
mainfrom
editor-media/asset-editing
Sep 7, 2026
Merged

Add in-context media asset editing#2905
khoinguyenpham04 merged 20 commits into
mainfrom
editor-media/asset-editing

Conversation

@khoinguyenpham04

@khoinguyenpham04 khoinguyenpham04 commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

Adds in-context Media Library asset editing to admin image workflows:

  • opens media details from featured image fields, Portable Text images, and gallery images without leaving the editor;
  • keeps browsing and editing inside one consistently sized media workspace;
  • supports metadata and focal-point updates, cropped copies, and original-image replacement;
  • refreshes edited previews while preserving per-use alt text, captions, layout, and display dimensions; and
  • adds accessible keyboard reordering for gallery images.

Type of change

  • Bug fix
  • Feature
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable). Do not include messages.po changes except in translation PRs — a workflow extracts catalogs on merge to main.
  • I have added and reviewed the user-facing changeset (if this PR changes a published package)
  • I have included screenshots below if this PR changes the UI

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: OpenAI Codex (GPT-5) with GPT-5.6 Terra used for iterative second-opinion review

Screenshots / test output

Verified on rebased commit f55ad62f7:

  • pnpm lint:json — 0 diagnostics
  • pnpm lint:quick
  • pnpm typecheck
  • pnpm format
  • 249 focused admin tests covering the media workspace, picker, image fields, rich-text images, galleries, and content editor

@changeset-bot

changeset-bot Bot commented Sep 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 7fc3022

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 17 packages
Name Type
@emdash-cms/admin Minor
emdash Minor
@emdash-cms/cloudflare Minor
@emdash-cms/sandbox-workerd Patch
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/do-demo-site Patch
@emdash-cms/do-solo-demo-site Patch
@emdash-cms/auth Minor
@emdash-cms/blocks Minor
create-emdash Minor
@emdash-cms/gutenberg-to-portable-text Minor
@emdash-cms/x402 Minor
@emdash-cms/auth-atproto Patch
@emdash-cms/release-service Patch
@emdash-cms/plugin-embeds Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Scope check

This PR changes 2,184 lines across 18 files. Large PRs are harder to review and more likely to be closed without review.

If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs.

See CONTRIBUTING.md for contribution guidelines.

@github-actions github-actions Bot added the review/needs-review No maintainer or bot review yet label Sep 4, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 4, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
docs f55ad62 Sep 04 2026, 08:53 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 4, 2026

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://editor-media-asset-editing.try.emdashcms.com, https://editor-media-asset-editing-emdash-playground.emdash-cms.workers.dev (commit 7fc3022)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://58805a02.try.emdashcms.com, https://58805a02-emdash-playground.emdash-cms.workers.dev 7fc3022 2026-09-06T22:44:13.839Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://46850c6c.try.emdashcms.com, https://46850c6c-emdash-playground.emdash-cms.workers.dev a69dfd9 2026-09-06T22:24:06.914Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://97a76e8b.try.emdashcms.com, https://97a76e8b-emdash-playground.emdash-cms.workers.dev 62068e2 2026-09-06T22:10:15.021Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://a7edc9cf.try.emdashcms.com, https://a7edc9cf-emdash-playground.emdash-cms.workers.dev 0799f68 2026-09-06T20:54:53.565Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://33c1bfa6.try.emdashcms.com, https://33c1bfa6-emdash-playground.emdash-cms.workers.dev f55ad62 2026-09-04T20:54:59.653Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://10616e09.try.emdashcms.com, https://10616e09-emdash-playground.emdash-cms.workers.dev 647dd3a 2026-09-04T14:53:35.358Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://30b15bbb.try.emdashcms.com, https://30b15bbb-emdash-playground.emdash-cms.workers.dev 716c425 2026-09-04T14:33:12.966Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://191fa851.try.emdashcms.com, https://191fa851-emdash-playground.emdash-cms.workers.dev 2e01061 2026-09-04T11:50:43.943Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://6d4cf75d.try.emdashcms.com, https://6d4cf75d-emdash-playground.emdash-cms.workers.dev bec58f8 2026-09-04T11:05:23.510Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://3804ad4b.try.emdashcms.com, https://3804ad4b-emdash-playground.emdash-cms.workers.dev f276ebb 2026-09-04T10:59:40.967Z Visit the dashboard ↗

View all previews: View all previews ↗

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds in-context Media Library asset editing to the admin image picker, image fields, and rich-text images/galleries by reusing MediaDetailPanel inside a new useMediaAssetEditor hook, and adds keyboard reordering to gallery blocks. The approach is sound: it stays inside the existing Kumo/dialog and TanStack Query patterns, preserves per-use metadata (alt/caption/layout) when creating cropped copies, and carefully coordinates the picker↔asset-dialog transitions with refs/generations so focus and selection state round-trip correctly.

I read the diff, the full changed source files, the media URL utilities, and the RBAC/role types. I did not run tests/formatting. The main code-level concern is a state-synchronization regression in the rich-text image panel; otherwise the implementation is careful and well-tested. There is also one small a11y/RTL polish item in the dark-variant slot.

Comment thread packages/admin/src/components/editor/ImageDetailPanel.tsx
Comment thread packages/admin/src/components/ImageFieldRenderer.tsx
@github-actions github-actions Bot added review/awaiting-author Reviewed; waiting on the author to respond and removed review/needs-review No maintainer or bot review yet labels Sep 4, 2026
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Overlapping PRs

This PR modifies files that are also changed by other open PRs:

This may cause merge conflicts or duplicated work. A maintainer will coordinate.

@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review and removed review/awaiting-author Reviewed; waiting on the author to respond labels Sep 4, 2026
@khoinguyenpham04
khoinguyenpham04 force-pushed the editor-media/asset-editing branch from e3bf84f to c514a01 Compare September 4, 2026 09:33
@pkg-pr-new

pkg-pr-new Bot commented Sep 4, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@2905

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@2905

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@2905

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@2905

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@2905

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@2905

emdash

npm i https://pkg.pr.new/emdash@2905

create-emdash

npm i https://pkg.pr.new/create-emdash@2905

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@2905

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@2905

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@2905

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@2905

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@2905

@emdash-cms/registry-moderation

npm i https://pkg.pr.new/@emdash-cms/registry-moderation@2905

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@2905

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@2905

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@2905

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@2905

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@2905

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@2905

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@2905

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@2905

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@2905

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@2905

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@2905

commit: 7fc3022

@khoinguyenpham04
khoinguyenpham04 force-pushed the editor-media/asset-editing branch from bec58f8 to 2e01061 Compare September 4, 2026 11:46
@khoinguyenpham04 khoinguyenpham04 added the bot:review Trigger an emdashbot code review on this PR label Sep 4, 2026
@khoinguyenpham04
khoinguyenpham04 force-pushed the editor-media/picker-foundation branch from 0d09146 to 7f3be67 Compare September 4, 2026 14:46
@khoinguyenpham04
khoinguyenpham04 force-pushed the editor-media/asset-editing branch from 716c425 to 647dd3a Compare September 4, 2026 14:46
@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Sep 4, 2026
Base automatically changed from editor-media/picker-foundation to main September 4, 2026 20:42
@khoinguyenpham04
khoinguyenpham04 force-pushed the editor-media/asset-editing branch from 647dd3a to d47162e Compare September 4, 2026 20:42
@khoinguyenpham04
khoinguyenpham04 force-pushed the editor-media/asset-editing branch from d47162e to f55ad62 Compare September 4, 2026 20:47
@khoinguyenpham04 khoinguyenpham04 changed the title editor media/asset editing Add in-context media asset editing Sep 4, 2026
@khoinguyenpham04 khoinguyenpham04 added the bot:review Trigger an emdashbot code review on this PR label Sep 4, 2026

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This re-review of the in-context media asset editing PR finds the approach still sound: the picker↔detail workspace handoff, per-use metadata preservation, and keyboard reordering all fit the existing Kumo/TanStack Query patterns. The prior state-synchronization regression in the rich-text image panel is addressed by keying the ImageDetailPanel reset effect on a stable nodeKey rather than on the attributes object identity, and the added ImageDetailPanel / GalleryDetailPanel tests cover node-switching and cropped-copy preservation.

I read the full diff, the changed source files, the media API/types, and the RBAC role constants. The code is careful and well-tested. The remaining issues are minor a11y/RTL polish items: a couple of decorative icons in newly-added or changed buttons are still missing aria-hidden="true", which leaves them exposed to screen readers alongside the buttons' text/aria-labels. The changeset is useful and the docs update matches the implemented behavior.

No blocking logic, security, or convention regressions were found.


Findings

  • [suggestion] packages/admin/src/components/ImageFieldRenderer.tsx:376

    The dark-variant “Add dark mode variant” button renders the decorative Moon icon without hiding it from assistive technologies. Sibling icons in this file already use aria-hidden="true"; this one should too so screen readers don’t announce both the icon and the button’s visible label.

    						icon={<Moon aria-hidden="true" />}
    
  • [suggestion] packages/admin/src/components/editor/GalleryDetailPanel.tsx:443

    The per-image Replace button in the gallery settings card renders the ImageSquare icon as visible content. Because the button already has a visible label, the icon is decorative and should be hidden from accessibility APIs to match the other icons in this panel.

    					icon={<ImageSquare aria-hidden="true" />}
    
  • [suggestion] packages/admin/src/components/editor/GalleryDetailPanel.tsx:392

    The thumbnail’s remove button has an aria-label, but the decorative Trash icon inside it is still exposed to assistive technologies. Add aria-hidden="true" so only the button label is announced.

    				<Trash className="h-3 w-3" aria-hidden="true" />
    

@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Sep 4, 2026
@github-actions github-actions Bot added review/awaiting-author Reviewed; waiting on the author to respond and removed review/needs-rereview Author pushed changes since the last review labels Sep 4, 2026
@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review and removed review/awaiting-author Reviewed; waiting on the author to respond labels Sep 6, 2026
@khoinguyenpham04 khoinguyenpham04 added the bot:review Trigger an emdashbot code review on this PR label Sep 6, 2026

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed the full diff for this feature PR, which adds in-context media asset editing to admin image pickers, image fields, and rich-text image/gallery nodes. It also updates MediaPickerModal, MediaDetailPanel, useMediaAssetEditor, plus tests, e2e coverage, docs, and a changeset.

The prior review asked for three decorative icons to be marked aria-hidden, and those are fixed in ImageFieldRenderer. I did not find any logic bugs, regressions, or broken contracts; RBAC checks, state reset keyed on nodeKey, and the intentional cache-only useQuery in GalleryPreviewImage all look reasonable.

I have two minor non-blocking suggestions: a few new decorative icons inside labelled buttons (and the empty-state icons) are still exposed to assistive tech because they lack aria-hidden="true", and the changeset should be tightened to match the repo's style guidance (past tense, one or two sentences).


Findings

  • [suggestion] packages/admin/src/components/editor/GalleryDetailPanel.tsx:261

    The Plus icon in the Add Images button is decorative because the button already has visible text. Decorative icons should be hidden from assistive technology to avoid redundant announcements.

    				icon={<Plus aria-hidden="true" />}
    
  • [suggestion] packages/admin/src/components/MediaDetailPanel.tsx:1800

    The Trash icon in the destructive Delete button is decorative because the visible label Delete / Deleting... provides the accessible name. Add aria-hidden="true" so screen readers don't also announce the icon.

    					icon={<Trash aria-hidden="true" />}
    
  • [suggestion] packages/admin/src/components/MediaDetailPanel.tsx:1811

    The ArrowsClockwise icon in the Replace image button is decorative because the button has the visible text Replace image. Hide it from assistive technology with aria-hidden="true".

    					icon={<ArrowsClockwise aria-hidden="true" />}
    
  • [suggestion] packages/admin/src/components/ImageFieldRenderer.tsx:514

    The ImageBroken icon next to the Image not found message is decorative. It should be hidden from assistive technology to avoid announcing a generic icon name alongside the already-clear text.

    						<ImageBroken className="h-5 w-5" aria-hidden="true" />
    
  • [suggestion] packages/admin/src/components/ImageFieldRenderer.tsx:539

    The ImageIcon icon inside the empty-state Select image button is decorative because the button already has the visible text Select image. Add aria-hidden="true" to keep the accessible name clean.

    					<ImageIcon className="h-8 w-8" aria-hidden="true" />
    
  • [suggestion] .changeset/bright-otters-edit.md:5

    AGENTS.md asks for changeset entries to be concise (one or two sentences) and in past tense. This entry uses present tense ("Adds") and is three sentences. Consider condensing and converting to past tense, e.g.:

    Added in-context Media Library asset editing to admin image pickers, image fields, and rich-text images and galleries, letting editors update metadata and focal points, create cropped copies, replace original image data, and reorder gallery images by keyboard without leaving the content editor.
    

@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Sep 6, 2026
@github-actions github-actions Bot added review/awaiting-author Reviewed; waiting on the author to respond and removed review/needs-rereview Author pushed changes since the last review labels Sep 6, 2026

@ascorbic ascorbic left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great!

@github-actions github-actions Bot added review/approved Approved; no new commits since and removed review/awaiting-author Reviewed; waiting on the author to respond labels Sep 7, 2026
@khoinguyenpham04

Copy link
Copy Markdown
Collaborator Author

Great!

Thanks!!

@khoinguyenpham04
khoinguyenpham04 merged commit de8b03a into main Sep 7, 2026
55 checks passed
@khoinguyenpham04
khoinguyenpham04 deleted the editor-media/asset-editing branch September 7, 2026 09:27
danielmlr added a commit to danielmlr/emdash that referenced this pull request Sep 7, 2026
Three conflicting changes landed on main: emdash-cms#2902 (save-conflict recovery),
emdash-cms#2891 (publishing and scheduling controls) and emdash-cms#2905 (in-context media
editing). Resolutions:

- ContentEditor: kept the read-only fieldsets and the `notice` slot, and
  took every new prop from main -- `publishingState`, `canSchedule`,
  `isScheduling`, `isUnscheduling`, `onOpenSchedule`, `onUnschedule` and
  `onMenuOpenChange` on `PublishActions` and `SettingsActionBar`, the
  schedule props folded into `publishingState` on `ContentSettingsPanel`,
  and the image-panel guard on the mobile close button.
- The save-conflict banner sits inside the disabled fieldset, so an entry
  held by someone else offers no "Save anyway" the server would refuse.
- router: the update mutation's `onError` recovers from a save conflict
  first, then reports the write error. `isSaveConflict` matches `CONFLICT`
  and the lock refusal is `ENTRY_LOCKED`, so the two do not overlap. Same
  order as the autosave mutation, which merged cleanly.
- publish-autosave-race: two assertions counted every POST or DELETE,
  which now includes the lock lease. Narrowed to the schedule endpoint,
  matching the filters this file already uses elsewhere.

Verified: typecheck 0, lint 0, oxfmt clean, core 6393 passed, admin 2025
passed. `editor-save-conflict` (5) and `editor/image-alignment` (2) fail
here, but they fail the same way with main's own ContentEditor.tsx and
router.tsx checked out in place of ours, so they are not from this merge.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants